#include "IMesh.h"


IMesh::IMesh(void )
{	
	mAnimationController= NULL;
	mMyNode = -1;			
	Animated = false;
	AbsoluteBone = NULL;	
}

void IMesh::CalculateGlobalTransform( SceneAnimNode* pInternalNode)
{
	// concatenate all parent transforms to get the global transform for this node
	pInternalNode->mGlobalTransform = pInternalNode->mLocalTransform;

	if(pInternalNode->mParent == -1)
		return;

	SceneAnimNode* node = Nodes[pInternalNode->mParent];	
	while(true)
	{		
		pInternalNode->mGlobalTransform = XMMatrixMultiply(pInternalNode->mGlobalTransform,node->mLocalTransform);
		if(node->mParent == -1)
			break;
		node = Nodes[node->mParent]; 
	}
}

void IMesh::UpdateTransforms( SceneAnimNode* pNode, XMMATRIX* pTransforms)
{
	// update node local transform
	int index = pNode->animationChannelIndex[mAnimationController->GetCurrentAnimationIndex() ];
	if(  index != -1)
	{
		pNode->mLocalTransform = pTransforms[index ];
	}

	// update global transform as well
	CalculateGlobalTransform( pNode);

	// continue for all children
	for( int i = 0; i < pNode->mChildren.size(); ++i)
		UpdateTransforms( Nodes[pNode->mChildren[i]], pTransforms);
}

void IMesh::UpdateSceneAnim()
{	
	if(AbsoluteBone  == NULL)
	{
		AbsoluteBone = (XMMATRIX*) _aligned_malloc(sizeof(XMMATRIX) * MAXBONES,16);			
		memset(AbsoluteBone,0,sizeof(XMMATRIX) * MAXBONES);
	}
	
	for(int i = 0; i < BoneMatrix.size(); i++)
	{
		XMFLOAT4X4 mat = BoneMatrix[i].first;		
		const SceneAnimNode* tempNode =  Nodes[BoneMatrix[i].second];				
				
		XMMATRIX matrix  = Nodes[mMyNode]->mGlobalTransform;		
		matrix = XMMatrixInverse(&XMMatrixDeterminant(matrix),matrix);				
		AbsoluteBone[i] = XMMatrixTranspose(XMMatrixMultiply(XMMatrixMultiply( (XMLoadFloat4x4(&mat)), tempNode->mGlobalTransform ) , matrix )) ;				
		//AbsoluteBone[i] = XMMatrixIdentity();
	}
}

void IMesh::Update(DWORD time)
{ 
	// no anim !!!
	if( !mAnimationController)
	{
		if(Nodes.size() > 0 )
		{
			CalculateGlobalTransform(Nodes[mMyNode]);
			*Transformation = Nodes[mMyNode]->mGlobalTransform;
		}
		return;
	}

	bool resp = mAnimationController->Update(time);	

	//if(resp)
	//{
	//	nodes.push_back(mRootNode);
	//	XMMATRIX* transforms =  mAnimationController->GetCurrentAnimationTransforms();
	//	while(!nodes.empty())
	//	{
	//		Linear(nodes.front() ,transforms,&nodes);
	//		nodes.remove(nodes.front());
	//	}		
	//}

	///mesma coisa, outra abordagem,mais rapida (!!!should not be !!!)
	if(resp)
		UpdateTransforms( Nodes[0], mAnimationController->GetCurrentAnimationTransforms());

	UpdateSceneAnim();	
}

void IMesh::SetTexture(ID3D11ShaderResourceView* texture, TextureTypes texMap)
{
	switch(texMap)
	{
	case TextureTypes::Diffuse:
		g_pTextureRV = texture;
		break;
	case TextureTypes::NormalMap:		
		break;
	}
	
}

void IMesh::SetTexture(const char* TextName , TextureTypes texMap)
{
	switch(texMap)
	{
	case TextureTypes::Diffuse:
		g_pTextureRV = EngineStuff::EAssetManagment->LoadTexture(TextName);		
		break;
	case TextureTypes::NormalMap:		
		break;
	}	
}

ID3D11ShaderResourceView* IMesh::GetTexture(TextureTypes texMap)
{
	switch(texMap)
	{
	case TextureTypes::Diffuse:
		return g_pTextureRV;
		break;
	case TextureTypes::NormalMap:
		return NULL;
		break;
	}
	return NULL;
}

IMesh::~IMesh(void)
{
	if(_vertexData.positionTextureNormalTangentBinormalVertex != NULL)
		delete _vertexData.positionTextureNormalTangentBinormalVertex ;

	if(_vertexData.positionTextureNormalVertex!= NULL)
		delete _vertexData.positionTextureNormalVertex;
		
	if(_indices!= NULL)
		delete _indices;
	
	if(modelName!= NULL)
		delete modelName;

	if(g_pVertexBuffer!=NULL)
		g_pVertexBuffer->Release();

	if(g_pIndexBuffer!=NULL)
		g_pIndexBuffer->Release();
	
	if(g_pTextureRV != NULL)
		g_pTextureRV->Release();

	if(Transformation!= NULL)
		_aligned_free(Transformation);

	if(AbsoluteBone != NULL)
	{
		_aligned_free(AbsoluteBone );
	}
	
}

HRESULT IMesh::Init(VertexData vertexData,int vertexCount,MeshTypeData type,XMMATRIX &transformation,const char* mmodelName ,const char* diffuseName ,D3D11_PRIMITIVE_TOPOLOGY topology,bool keepDataInRamMemory )
{
	return IMesh::Init(vertexData,vertexCount,type,NULL,0,transformation,mmodelName,diffuseName,topology,keepDataInRamMemory);
}

HRESULT IMesh::Init(VertexData vertexData,int vertexCount,MeshTypeData type,UINT* indices, int indexCount,XMMATRIX &transformation,const char* mmodelName ,const char* diffuseName ,D3D11_PRIMITIVE_TOPOLOGY topology,bool keepDataInRamMemory )
{	
	modelName  = mmodelName ;
	meshTypeData = type;
	HRESULT hr = S_OK;
	int sizeofVert;
	void* data;
	_indexCount = indexCount;
	_vertexCount = vertexCount;
	if(type == MeshTypeData::PositionTextureNormal)
	{
		sizeofVert = sizeof( PositionTextureNormalVertex );
		data = vertexData.positionTextureNormalVertex;
		stride = sizeof( PositionTextureNormalVertex );
	}
	else if(type == MeshTypeData::PositionTextureNormalTangentBinormal)
	{
		sizeofVert = sizeof( PositionTextureNormalTangentBinormalVertex );		
		data = vertexData.positionTextureNormalTangentBinormalVertex;
		stride = sizeof( PositionTextureNormalTangentBinormalVertex );
	}
	else if(type == MeshTypeData::PositionTextureNormalTangentBinormalAnimated)
	{
		sizeofVert = sizeof( PositionTextureNormalTangentBinormalAnimatedVertex);		
		data = vertexData.positionTextureNormalTangentBinormalAnimatedVertex;
		stride = sizeof( PositionTextureNormalTangentBinormalAnimatedVertex );
	}
	else
	{
		///mesh type invalid
		assert(false);
	}


	///Create VertexBuffer
	D3D11_BUFFER_DESC bd;
    ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;	
	bd.ByteWidth = sizeofVert * vertexCount;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    D3D11_SUBRESOURCE_DATA InitData;
    ZeroMemory( &InitData, sizeof(InitData) );
    InitData.pSysMem = data;
	hr = EngineStuff::EGraphicsDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
    CheckError(hr);

	if(indices)
	{
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.ByteWidth = sizeof( UINT ) * indexCount;
		bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		InitData.pSysMem = indices;
		hr = EngineStuff::EGraphicsDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer );
		CheckError(hr);
	}
	else
	{
		g_pIndexBuffer = NULL;
		indexCount = 0;
	}

	// Load the Texture
	if(diffuseName!=NULL && strcmp(diffuseName,DEFAULTTEXTURE) != 0)
	{
		hr = D3DX11CreateShaderResourceViewFromFile( EngineStuff::EGraphicsDevice, diffuseName, NULL, NULL, &g_pTextureRV, NULL );
		if(FAILED(hr))
		{			
#ifdef SHOW_ERRORS
			string mes(" : NOT FOUND");					
			string name(diffuseName);
			name += mes;
			MessageBox(NULL,name.c_str(),"ERROR",MB_OK );
#endif
			g_pTextureRV = EngineStuff::EAssetManagment->ColorTexture(512,512,0,0,1);
			hr = S_OK; ///recuperavel
		}		
	}
	else
	{
		///default
		g_pTextureRV = EngineStuff::EAssetManagment->ColorTexture(512,512,0,0,1);
	}

	_topology = topology;
	Transformation = (XMMATRIX*) _aligned_malloc(sizeof(XMMATRIX), 16);
	*Transformation = transformation;

	if(keepDataInRamMemory)
	{
		_vertexData = vertexData;
		_indices = indices;		
	}
	else
	{
		delete [] data;
		if(indices)
			delete [] indices;
		data = NULL;
		indices = NULL;
	}
	return hr;
}

void IMesh::SetMeshOnDevice()
{	
	// Set vertex buffer    
    UINT offset = 0;	
	EngineStuff::EImediateDeviceContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

	// Set index buffer
	if(g_pIndexBuffer != NULL)
		EngineStuff::EImediateDeviceContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

    // Set primitive topology
    EngineStuff::EImediateDeviceContext->IASetPrimitiveTopology(_topology);
}
void IMesh::DrawMesh(void)
{
	if(g_pIndexBuffer == NULL)
	{
		if(_vertexCount != 0)
		{
			EngineStuff::EImediateDeviceContext->Draw(_vertexCount,0);
		}
		else
		{
			EngineStuff::EImediateDeviceContext->DrawAuto();
		}
	}
	else
	{
		EngineStuff::EImediateDeviceContext->DrawIndexed(_indexCount,0,0);		
	}
}
